/**
 * 应用类，作为整个应用的入口类
 * 包含：
 * 事件监听分发
 * 循环主方法
 */
namespace panda {
  export class Application implements EventListenerObject{
    protected _start : boolean = false; // 标记当前Application是否进入不间断地循环状态
    protected _requestId : number = -1; // 记录requestAnimationFrame返回的大于0的ID号
    protected _lastTime !: number;
    protected _startTime !: number;
    protected _isMouseDown : boolean;

    public canvas:HTMLCanvasElement;
    public isSupportMouseMove:boolean;
    public timers:Array<Timer> = [];

    private _timeId:number = -1; // id从0开始是有效ID，负数为无效id
    private _fps:number = 0;

    public constructor(canvas:HTMLCanvasElement) {
      this.canvas = canvas;
      this.canvas.addEventListener("mousedown", this, false);
      this.canvas.addEventListener("mouseup", this, false);
      this.canvas.addEventListener("mousemove", this, false);
      // 键盘事件不能在canvas里面触发，所以如下代码
      window.addEventListener("keydown", this, false);
      window.addEventListener("keyup", this, false);
      window.addEventListener("keypress", this, false);
      // 初始化时mouseDown为false
      this._isMouseDown = false;
      this.isSupportMouseMove = false;
    }

    /**
     * 开始动画循环
     */
    public start() : void {
      if ( ! this._start ) {
        this._start = true;
        this._requestId = -1;  // 刚开始，初始化为-1
        this._lastTime = -1;
        this._startTime = -1;
        this._requestId = requestAnimationFrame((elapsedMsec:number):void=>{
          this.step(elapsedMsec); // 启动step方法
        });
      }
    } 

    /**
     * 结束动画
     */
    public stop() : void {
      if ( this._start ) {
        cancelAnimationFrame(this._requestId);
        this._requestId = -1;
        this._lastTime = -1;
        this._startTime = -1;
        this._start = false;
      }
    }

    /**
     * 判断是否正在执行动画循环
     */
    public isRunning() : boolean {
      return this._start;
    }

    /**
     * 每一步进行的操作
     * @param timeStamp 当前时间戳
     */
    protected step(timeStamp:number) : void {
      if ( this._startTime === -1 ) this._startTime = timeStamp;
      if ( this._lastTime === -1 ) this._lastTime = timeStamp;
      // 计算当前时间点与第一次调用step时间点的差，以毫秒为单位
      let elapsedMsec:number = timeStamp - this._startTime;
      // 计算当前时间点与上一次调用地时间点的差，以秒为单位
      let intervalSec:number = timeStamp-this._lastTime;
      // 计算fps
      if (intervalSec !== 0) {
        this._fps = 1000.0 / intervalSec;
      }
      intervalSec /= 1000.0; // 转化为秒为单位
      // 记录上一次时间戳
      this._lastTime = timeStamp;
      // 触发多个定时任务
      this._handleTimers(intervalSec);
      console.log("距离第一次时间:"+elapsedMsec+" 距离上一次时间:"+intervalSec);
      // 先更新
      this.update(elapsedMsec, intervalSec);
      // 后渲染
      this.render();
      // 递归调用，形成循环操作
      requestAnimationFrame((elapsedMsec:number):void => {
        this.step(elapsedMsec);
      });
    }

    /**
     * 将相对浏览器viewport表示的点变换到相对canvas表示的点
     * @param evt 
     */
    private _viewportToCanvasCoordinate(evt:MouseEvent):Vec2 {
      if ( this.canvas ) {
        let rect:ClientRect = this.canvas.getBoundingClientRect();
        if ( evt.type === "mousedown" ){
          // console.log("boundingClientRect:"+JSON.stringify(rect));
          // console.log("clientX:"+evt.clientX+" clientY:"+evt.clientY);
        }
        let borderLeftWidth:number = 0;
        let borderTopWidth:number = 0;
        let paddingLeft:number = 0;
        let paddingTop:number = 0;
        // 获取触发鼠标事件的元素，这里总是HTMLCanvasElement
        if (evt.target) {
          // 获取canvas的style对象，因为返回的值是字符串，所以需要进行parseInt
          let decl:CSSStyleDeclaration = window.getComputedStyle(evt.target as HTMLElement);
          let strNumber:string|null = decl.borderLeftWidth;
          if ( strNumber !== null ){
            borderLeftWidth = parseInt(strNumber,10);
          }
          strNumber = decl.borderTopWidth;
          if ( strNumber !== null ){
            borderTopWidth = parseInt(strNumber,10);
          }
          strNumber = decl.paddingLeft;
          if ( strNumber !== null ){
            paddingLeft = parseInt(strNumber, 10);
          }
          strNumber = decl.paddingTop;
          if ( strNumber !== null ){
            paddingTop = parseInt(strNumber, 10);
          }
        }
        let x:number = evt.clientX - rect.left - borderLeftWidth - paddingLeft;
        let y:number = evt.clientY - rect.top - borderTopWidth - paddingTop;
        return Vec2.create(x, y);
      }
      alert('canvas is null');
      throw new Error('canvas is null');
    }

    /**
     * 将DOM Event对象信息转换为自定义的CanvasMouseEvent事件
     * @param evt 
     */
    private _toCanvasMouseEvent(evt:Event):CanvasMouseEvent {
      let event:MouseEvent = evt as MouseEvent;
      let mousePosition:Vec2 = this._viewportToCanvasCoordinate(event);
      let canvasMouseEvent:CanvasMouseEvent = new CanvasMouseEvent(mousePosition,
        event.button, event.altKey, event.ctrlKey, event.shiftKey);
      return canvasMouseEvent;
    }

    /**
     * 将DOM Event对象信息转换为自定义的KeyBoard事件
     * @param evt 
     */
    private _toCanvasKeyBoardEvent(evt:Event):CanvasKeyBoardEvent {
      let event:KeyboardEvent = evt as KeyboardEvent;
      let canvasKeyBoardEvent:CanvasKeyBoardEvent = new CanvasKeyBoardEvent(
        event.key, event.keyCode, event.repeat, event.altKey, event.ctrlKey, event.shiftKey
      );
      return canvasKeyBoardEvent;
    }

    /**
     * 事件分发
     * @param evt 
     */
    public handleEvent(evt:Event):void {
      switch (evt.type) {
        case "mousedown": 
          this._isMouseDown = true;
          this.dispatchMouseDown(this._toCanvasMouseEvent(evt));
          break;
        case "mouseup":
          this._isMouseDown = false;
          this.dispatchMouseUp(this._toCanvasMouseEvent(evt));
          break;
        case "mousemove":
          if ( this.isSupportMouseMove ) {
            this.dispatchMouseMove(this._toCanvasMouseEvent(evt));
          }
          if ( this._isMouseDown ){
            this.dispatchMouseDrag(this._toCanvasMouseEvent(evt));
          }
          break;
        case "keypress":
          this.dispatchKeyPress(this._toCanvasKeyBoardEvent(evt));
          break;
        case "keydown":
          this.dispatchKeyDown(this._toCanvasKeyBoardEvent(evt));
          break;
        case "keyup":
          this.dispatchKeyUp(this._toCanvasKeyBoardEvent(evt));
          break;
      }
    }

    /**
     * 删除某个计时器
     * 从timers数组里去找看是否存在该计时器，然后将要删除的计时器的enabled设置为
     * false，相当于软删除，也不会调整timers数组内容，下次增加新的timer时，会先
     * 查找enabled为false的Timer
     * @param id 计时器的ID号
     */
    public removeTimer(id:number):boolean {
      let found:boolean = false;
      for (let i=0; i<this.timers.length; i++){
        if ( this.timers[i].id === id ){
          let timer:Timer = this.timers[i];
          timer.enabled = false;
          found = true;
          break;
        }
      }
      return found;
    }

    /**
     * 增加一个计时器
     * @param callback 回调函数
     * @param timeout 
     * @param onlyOnce 
     * @param data 
     */
    public addTimer(callback:TimerCallback, timeout:number=1.0,
      onlyOnce:boolean=false, data:any=undefined):number {
      let timer:Timer;
      let found:boolean = false;
      for ( let i=0; i<this.timers.length; i++){
        let timer:Timer = this.timers[i];
        if ( timer.enabled === false ){
          timer.callback = callback;
          timer.callbackData = data;
          timer.timeout = timeout;
          timer.countdown = timeout;
          timer.enabled = true;
          timer.onlyOnce = onlyOnce;
          return timer.id;
        }
      }
      timer = new Timer(callback);
      timer.callbackData = data;
      timer.timeout = timeout;
      timer.countdown = timeout;
      timer.enabled = true;
      timer.onlyOnce = onlyOnce;
      timer.id = ++this._timeId;
      this.timers.push(timer);
      return timer.id;
    }

    private _handleTimers(intervalSec:number):void {
      for ( let i=0; i<this.timers.length; i++ ){
        let timer:Timer = this.timers[i];
        if ( timer.enabled === false ){
          continue;
        }
        timer.countdown -= intervalSec;
        if ( timer.countdown < 0.0 ){
          timer.callback(timer.id, timer.callbackData);
          if ( timer.onlyOnce === false ){
            timer.countdown = timer.timeout; 
          } else {
            this.removeTimer(timer.id);
          }
        }
      }
    }

    /**
     * 获取fps值
     */
    public get fps() {
      return this._fps;
    }

    /**
     * 虚方法，每帧更新操作，提供给子类继承
     * @param elapsedMsec 距第一次的时间间隔，毫秒
     * @param intervalSec 距上一次的时间间隔，秒
     */
    public update(elapsedMsec:number, intervalSec:number) : void {}

    /**
     * 虚方法，每帧渲染内容，提供给子类继承
     */
    public render() : void {}

    protected dispatchMouseDown(evt:CanvasMouseEvent):void {}
    protected dispatchMouseUp(evt:CanvasMouseEvent):void {}
    protected dispatchMouseMove(evt:CanvasMouseEvent):void {}
    protected dispatchMouseDrag(evt:CanvasMouseEvent):void {}
    protected dispatchKeyDown(evt:CanvasKeyBoardEvent):void {}
    protected dispatchKeyUp(evt:CanvasKeyBoardEvent):void {}
    protected dispatchKeyPress(evt:CanvasKeyBoardEvent):void {}
  }
}